
var PENDING = 'pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';


const resolvePromise = (promise, x, resolve, reject) => {
  // 如果 promise 和 x 指向同一对象，以 TypeError 为据因拒绝执行 promise
  // 这是为了防止死循环
  if (promise === x) {
    return reject(new TypeError('The promise and the return value are the same'))
  }
  if (x instanceof MyPromise) {
    // 如果 x 为 Promise ，则使 promise 接受 x 的状态
    // 也就是继续执行x，如果执行的时候拿到一个y，还要继续解析y
    // 这个if跟下面判断then然后拿到执行其实重复了，可有可无
    x.then((y) => {
      resolvePromise(promise, y, resolve, reject)
    }, reject)
  } else if (typeof x === 'object' || typeof x === 'function') {
    // 这个坑是跑测试的时候发现的，如果x是null，应该直接resolve
    if (x === null) {
      return resolve(x);
    }
    let then = null
    try {
      then = x.then
    } catch (error) {
      reject(error)
    }
    let called = false;
    if (typeof then === 'function') {
      try {
        then.call(x, (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject)
          }, (r) => {
            if (called) return;
            called = true;
            reject(r)
          }
        )
      } catch (error) {
        if (called) return;
        reject(error)
      }
    } else {
      resolve(x)
    }
  } else {
    resolve(x)
  }
}

function MyPromise(fn) {
  this.status = PENDING;
  this.value = null;
  this.reason = null;
  this.onFullfilledCallbacks = [];
  this.onRejectedCallbacks = [];
  const that = this;
  function resolve(res) {
    if(that.status === PENDING) {
      that.status = FULFILLED
      that.value = res
      that.onFullfilledCallbacks.forEach((cb) => {
        cb(that.value)
      })
    }
  }

  function reject(err) {
    if(that.status === PENDING) {
      that.status = REJECTED
      that.reason = err
      that.onRejectedCallbacks.forEach((cb) => {
        cb(that.reason)
      })
    }
  }

  try {
    fn(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

MyPromise.prototype.then = function (onFullfilled, onRejected) {
  const that = this;
  let realOnFullfilled = onFullfilled;
  if (typeof realOnFullfilled !== 'function') {
    realOnFullfilled = (value) => {
      return value
    }
  }
  let realOnRejected = onRejected;
  if (typeof realOnRejected !== 'function') {
    realOnRejected = (reason) => {
      throw reason
    }
  }

  if (that.status === FULFILLED) {
    const promise2 = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          if (typeof onFullfilled !== 'function') {
            resolve(that.value)
          } else {
            const x = realOnFullfilled(that.value)
            resolvePromise(promise2, x, resolve, reject) // 调用Promise 解决过程
          }
        } catch (error) {
          reject(error)
        }
      }, 0)
    })
    return promise2;
  }

  if (that.status === REJECTED) {
    const promise2 = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          if (typeof onRejected !== 'function') {
            reject(that.reason)
          } else {
            const x = realOnRejected(that.reason)
            resolvePromise(promise2, x, resolve, reject)
          }
        } catch (error) {
          reject(error)
        }
      }, 0)
    })
    return promise2;
  }

  if (that.status = PENDING) {
    const promise2 = new MyPromise((resolve, reject) => {
      that.onFullfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            if (typeof onFullfilled !== 'function') {
              resolve(that.value)
            } else {
              const x = realOnFullfilled(that.value)
              resolvePromise(promise2, x, resolve, reject)
            }
          } catch (error) {
            reject(error)
          }
        }, 0)
      })
      that.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            if (typeof onRejected !== 'function') {
              reject(that.reason)
            } else {
              const x = realOnRejected(that.reason)
              resolvePromise(promise2, x, resolve, reject)
            }
          } catch (error) {
            reject(error)
          }
        }, 0)
      })
    })
    return promise2
  }
}

MyPromise.deferred = function() {
  var result = {};
  result.promise = new MyPromise(function(resolve, reject){
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}

module.exports = MyPromise